#![no_std]
use core::fmt::{self, Write};
pub use core::file;
pub use core::line;
use rtt_kernel_mutex::KMutex;
use core::ptr;

struct StdOut;
static OUT: KMutex<StdOut> = KMutex::new(StdOut{});

impl fmt::Write for StdOut {
    #[cfg(feature = "qemu")]
    fn write_str(&mut self, _s: &str) -> fmt::Result {

        let _base_uart0 = 0x10009000;
        let _uart0_dr: *mut u32 = _base_uart0 as _;
        let _uart0_fr: *mut u32 = (_base_uart0 + 0x18) as _;
        let _uart0_cr: *mut u32 = (_base_uart0 + 0x30) as _;
        let _uart0_imsc: *mut u32 = (_base_uart0 + 0x38) as _;
        let _uart0_icr: *mut u32 = (_base_uart0 + 0x44) as _;

        let _txff = 0x20;
        let _rxfe = 0x10;

        unsafe {
            for i in _s.bytes() {
                loop {
                    if ptr::read_volatile(_uart0_fr as *const u32) & _txff == 0 {
                        *_uart0_dr = i as u32;
                        break;
                    }
                }
            }
        }

        Ok(())
    }
}

pub fn io_init() {
    #[cfg(feature = "qemu")]
    {
        let _base_uart0 = 0x10009000;
        let _uart0_cr: *mut u32 = (_base_uart0 + 0x30) as _;
        unsafe {
            *_uart0_cr = (1 << 0) | (1 << 8) | (1 << 9)
        }
    }
}

pub fn _print(args: fmt::Arguments) {
    let mut out = OUT.lock();
    out.write_fmt(args).unwrap();
}

pub fn _print_unlock(args: fmt::Arguments) {
    StdOut.write_fmt(args).unwrap();
}

#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ({
        $crate::_print(format_args!($($arg)*));
    });
}

#[macro_export]
macro_rules! print_unlock {
    ($($arg:tt)*) => ({
        $crate::_print_unlock(format_args!($($arg)*));
    });
}

#[macro_export]
macro_rules! dbg {
    () => {
        #[cfg(feature = "debug")]
        $crate::print!("[{}:{}]\n", $crate::file!(), $crate::line!());
    };
    ($val:expr $(,)?) => {
        #[cfg(feature = "debug")]
        match $val {
            tmp => {
                $crate::print!("[{}:{}] {} = {:#?}\n",
                    $crate::file!(), $crate::line!(), core::stringify!($val), &tmp);
                tmp
            }
        }
    };
    ($($val:expr),+ $(,)?) => {
        #[cfg(feature = "debug")]
        ($($crate::dbg!($val)),+,)
    };
}

#[panic_handler]
#[inline(never)]
fn panic(info: &core::panic::PanicInfo) -> ! {
    print_unlock!("{:}", info);
    loop{}
}